package cn.clx.ymcc.service.impl;

import cn.clx.ymcc.domain.Course;
import cn.clx.ymcc.domain.CourseMarket;
import cn.clx.ymcc.domain.KillActivity;
import cn.clx.ymcc.domain.KillCourse;
import cn.clx.ymcc.dto.KillParamDto;
import cn.clx.ymcc.dto.KillPreOrderParamsDto;
import cn.clx.ymcc.mapper.KillCourseMapper;
import cn.clx.ymcc.service.IKillActivityService;
import cn.clx.ymcc.service.IKillCourseService;
import cn.clx.ymcc.util.AssertUtil;
import cn.clx.ymcc.util.CodeGenerateUtils;
import cn.clx.ymcc.vo.CourseInfo;
import cn.clx.ymcc.vo.OrderCourseInfoVo;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.redisson.client.RedisClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author clx
 * @since 2022-08-04
 */
@Service
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {

    @Autowired
    private IKillActivityService killActivityService;
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;
    @Autowired
    private RedissonClient redissionClient;
    /**
     * 课程加入秒杀活动
     * @param killCourse
     */
    @Override
    public void save(KillCourse killCourse) {
        //还要防止课程重复加入秒杀活动
        KillCourse course =selectByActivityIdAndCourseId(killCourse.getActivityId(),killCourse.getCourseId());
        AssertUtil.isNull(course,"该课程不能重复加入该秒杀活动");

        //校验 jsr303  -----不做
        KillActivity killActivity = killActivityService.selectById(killCourse.getActivityId());
        AssertUtil.isNotNull(killActivity,"该秒杀活动出错");
        killCourse.setStartTime(killActivity.getStartTime());
        killCourse.setEndTime(killActivity.getEndTime());
        killCourse.setKillLimit(1);
        killCourse.setTimeStr(killActivity.getTimeStr());
        killCourse.setPublishStatus(0);
        killCourse.setCreateTime(new Date());
        insert(killCourse);
    }

    /**
     * 查询秒杀课程
     * @param
     * @return
     */
    @Override
    public List<KillCourse> online() {
        //能拿到所有key值为   activity:xx  的values
        Set<Object> keys = redisTemplate.keys("activity:*");
        List killCourses = new ArrayList<>();
        for (Object key : keys) {
            //拿到大key对应的所有小key的值
            List values = redisTemplate.opsForHash().values(key);
            //这里不能用add，相当于两次添加，用了一个数组的形式隔开  要用addALL
            killCourses.addAll(values);
        }

        return killCourses;
    }

    /**
     * 查询秒杀课程详情页
     * @param activityId
     * @param killCourseId
     * @return
     */
    @Override
    public KillCourse selectKillCourseDetail(Long activityId, Long killCourseId) {
        //拿到存储在redis中的课程详情数据
        return (KillCourse) redisTemplate.opsForHash().get("activity:" + activityId, killCourseId.toString());
    }
    /**
     * 创建秒杀预购订单
     * @param dto
     * @return
     */
    @Override
    public String killCourse(KillParamDto dto) {
        Long activityId = dto.getActivityId();
        Long killCourseId = dto.getKillCourseId();
        KillCourse killCourse = selectKillCourseDetail(activityId, killCourseId);
        AssertUtil.isNotNull(killCourse,"网络繁忙，请稍后再试");
        AssertUtil.isTrue(killCourse.isKilling(),"非法秒杀");
        //必须要登录才能秒杀
        Long loginId=3l;
        //不能重复秒杀，一个用户只能秒杀一次
        String repeat = activityId+":"+killCourseId+":"+loginId;
        Object one = redisTemplate.opsForValue().get(repeat);
        AssertUtil.isNull(one,"该商品只能秒杀一个哦");
        //扣减信号量 成功
        String key = activityId+":"+killCourseId;
        RSemaphore semaphore = redissionClient.getSemaphore(key);

        //扣减信号量
        Integer killCount =1; //秒杀数量
        boolean OK = semaphore.tryAcquire(killCount);
        AssertUtil.isTrue(OK,"网络繁忙，请稍后再试");
        //创建预购订单
        String orderNo = CodeGenerateUtils.generateOrderSn(loginId);
        KillPreOrderParamsDto killPreOrderParamsDto = new KillPreOrderParamsDto(
                orderNo,
                dto.getKillCourseId(),
                dto.getActivityId(),
                killCount,
                killCourse.getKillPrice(),
                loginId,
                killCourse.getCourseId(),//课程ID
                killCourse.getCourseName(),
                killCourse.getCoursePic()
        );
        //存储预购订单到redis中
        redisTemplate.opsForValue().set(orderNo,killPreOrderParamsDto);
        //存储该用户的秒杀记录到redis中防止重复秒杀
        redisTemplate.opsForValue().set(repeat,orderNo);
        return orderNo;
    }

    /**
     * 秒杀确认订单，课程数据回显
     * @param orderNo
     * @return
     */
    @Override
    public OrderCourseInfoVo oneByOrder(String orderNo) {
        KillPreOrderParamsDto dto = (KillPreOrderParamsDto) redisTemplate.opsForValue().get(orderNo);
        AssertUtil.isNotNull(dto,"网络繁忙，请稍后再试");
        Course course = new Course();
        course.setId(dto.getCourseId());
        course.setName(dto.getCourseName());
        course.setPic(dto.getCoursePic());
        CourseMarket courseMarket = new CourseMarket();
        courseMarket.setPrice(dto.getKillAmount());
        List<CourseInfo> courseInfos=new ArrayList<CourseInfo>();
        CourseInfo courseInfo = new CourseInfo(course, courseMarket);
        courseInfos.add(courseInfo);
        OrderCourseInfoVo orderCourseInfoVo = new OrderCourseInfoVo(courseInfos,dto.getKillAmount());
        return orderCourseInfoVo;
    }


    /**
     * 基于活动id 和 课程id 查询
     * @param activityId
     * @param courseId
     * @return
     */
    private KillCourse selectByActivityIdAndCourseId(Long activityId, Long courseId) {
        EntityWrapper<KillCourse> wrapper = new EntityWrapper<>();
        wrapper.eq("activity_id",activityId);
        wrapper.eq("course_id",courseId);
        return selectOne(wrapper);
    }
}
